﻿using System;
using System.Collections;
using System.Collections.Generic;
using Borg.AddOn;

namespace Borg
{
    [Serializable]
    public abstract class BehaviorCollection : ICollection<Behavior>
    {
        private Behavior[] _behaviors;
        private readonly INotifierNotificationHook _notificationHook;

        protected abstract Runtime Runtime { get; }

        public int Count
        {
            get { return _behaviors.Length; }
        }

        public bool IsReadOnly
        {
            get { return false; }
        }

        protected BehaviorCollection(INotifierNotificationHook notificationHook)
        {
            _notificationHook = notificationHook;
            _behaviors = new Behavior[0];
        }

        protected abstract void Bind(Behavior behavior);
        protected abstract void Unbind(Behavior behavior);

        public void Add(Behavior behavior)
        {
            if (behavior == null)
                throw new ArgumentNullException("behavior");

            if (ArrayEx.Contains(_behaviors, behavior))
                throw new ArgumentException("Behavior is already in behavior collection");

            _behaviors = ArrayEx.Add(_behaviors, behavior);
            _notificationHook.OnHandlerAdded(behavior);
            Bind(behavior);
        }

        public bool Remove(Behavior behavior)
        {
            if (behavior == null)
                throw new ArgumentNullException("behavior");

            if (_behaviors != null && _behaviors.Length != 0)
            {
                var behaviors = ArrayEx.Remove(_behaviors, behavior) ?? new Behavior[0];
                var szdiff = behaviors.Length < _behaviors.Length;
               
                _behaviors = behaviors;

                if (szdiff)
                {
                    Unbind(behavior);
                    _notificationHook.OnHandlerRemoved(behavior);
                    return true;
                }
            }

            return false;
        }
        
        public void Clear()
        {
            var behaviors = _behaviors;

            _behaviors = new Behavior[0];

            for (var i = behaviors.Length - 1; i >= 0; i--)
                Unbind(behaviors[i]);
        }

        public bool Contains(Behavior behavior)
        {
            if (behavior == null)
                throw new ArgumentNullException("behavior");

            return Array.Exists(_behaviors, item => item.Equals(behavior));
        }

        public void CopyTo(Behavior[] newBehaviors, int arrayIndex)
        {
            Array.Copy(_behaviors, 0, newBehaviors, arrayIndex, _behaviors.Length);
        }

        public IEnumerator<Behavior> GetEnumerator()
        {
            return ((IEnumerable<Behavior>) _behaviors).GetEnumerator();
        }
        
        IEnumerator IEnumerable.GetEnumerator()
        {
            return GetEnumerator();
        }
    }
}
